#pragma once
#include "DockingDlgInterface.h"
#include "PluginInterface.h"
#include "resource.h"
#include "TreeViewCtrl.h"
#include "ScintillaEditor.h"
#include "JsonHandler.h"
#include "DebugTrace.h"
#include <string>
#include <memory>
#include <vector>
#include <optional>
#include <bitset>

struct HtreeItemInfo
{
    int       peripheral_group;
    int       register_group;
    int       field_group;
    HTREEITEM hti;
};


class rj_data_field_cls
{
public:
    int bit_offset = -1;
    int bit_width  = -1;
};

class rj_data_register_cls
{
public:
    int                            address_offset = 0;
    int                            size           = 0;
    std::vector<rj_data_field_cls> fields;
};

class rj_data_peripheral_cls
{
public:
    INT64                             base_address = 0;
    size_t                            size         = 0;
    unsigned char                     *byte_arr    = 0;
    std::vector<rj_data_register_cls> regs;
};

class SVDField
{
private:
    std::bitset<32> field_bitset;
    unsigned long   m_ul;

public:
    SVDField(unsigned char * char_bytes, int from, int width) {
        unsigned long val;
        memcpy(&val, char_bytes, 4);
        field_bitset = std::bitset<32>(val);
        unsigned long bit_end     = (unsigned long)from + (unsigned long)width;
        unsigned long bit_left    = (unsigned long)32 - (unsigned long)bit_end;
        unsigned long bit_right   = bit_left + (unsigned long)from;
        unsigned long left_shift  = bit_left;
        unsigned long right_shift = bit_right;
        field_bitset <<= left_shift;
        if (right_shift < 32)
            field_bitset >>= right_shift;
        m_ul         = field_bitset.to_ulong();
    };
    unsigned long to_ulong()
    {
        return m_ul;
    }
    virtual ~SVDField();
};

class JsonViewDlg : public DockingDlgInterface
{
    enum class eButton
    {
        eRefresh,
        eValidate,
        eFormat,
        eSearch
    };

    enum class eMethod
    {
        FormatJson,
        GetCompressedJson,
        ParseJson,
        ValidateJson
    };

public:
    JsonViewDlg(HINSTANCE hInstance, const NppData &nppData, const bool &isReady, int nCmdId, std::shared_ptr<Setting> &pSetting);
    virtual ~JsonViewDlg();

    void ShowDlg(bool bShow);
    void FormatJson();
    void CompressJson();
    void HandleTabActivated();

    HTREEITEM InsertToTree(HTREEITEM parent, const std::string &text);
    void      AppendNodeCount(HTREEITEM node, unsigned elementCount, bool bArray);
    void      TestNode(void);

    auto GetTreeNodeName(HTREEITEM n) const -> std::wstring;
    std::vector<struct HtreeItemInfo> m_lHtreeItems;
    std::string                       f_json_view_with_value;
    std::vector<UINT32> m_i_baseaddr;

private:
    void DrawJsonTree();
    void FillArray(class rj_data_peripheral_cls & rj_peripheral);

    class rj_data_field_cls GetFieldInfo(struct HtreeItemInfo htreeItem, class rj_data_register_cls &regs);
    class rj_data_register_cls GetRegisterInfo(struct HtreeItemInfo &htreeItem, class rj_data_peripheral_cls peripherals);
    class rj_data_peripheral_cls GetPeripheralInfo(struct HtreeItemInfo &htreeItem);


    void HighlightAsJson(bool bForcefully = false) const;
    auto PopulateTreeUsingSax(HTREEITEM tree_root, const std::string &jsonText) -> std::optional<std::wstring>;

    void ValidateJson();

    void UpdateNodePath(HTREEITEM htiNode);

    void SearchInTree();

    void PrepareButtons();
    void SetIconAndTooltip(eButton ctrlType, const std::wstring &toolTip);

    void AdjustDocPanelSize(int nWidth, int nHeight);

    // Context menu related functions
    void ShowContextMenu(int x, int y);
    void ShowContextMenu(HTREEITEM htiNode, LPPOINT lppScreen);
    void ContextMenuExpand(bool bExpand);

    auto CopyName() const -> std::wstring;
    auto CopyKey() const -> std::wstring;
    auto CopyValue() const -> std::wstring;
    auto CopyPath() const -> std::wstring;

    int  ShowMessage(const std::wstring &title, const std::wstring &msg, int flag, bool bDontShow = false);
    void ReportError(const Result &result);

    void ToggleMenuItemState(bool bVisible);

    void ShowControls(const std::vector<DWORD> &ids, bool show);
    void EnableControls(const std::vector<DWORD> &ids, bool enable);

    void HandleTreeEvents(LPARAM lParam);

    auto GetFormatSetting() const -> std::tuple<LE, LF, char, unsigned>;

    bool CheckForTokenUndefined(eMethod method, std::string selectedText, Result &res, HTREEITEM tree_root);

    bool m_bTreeViewOK = false;
    HTREEITEM m_HTREE_RootNode = nullptr;
    std::vector<rj_data_peripheral_cls> rj_data_peripherals;

protected:
    virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);

private:
    int         m_nDlgId      = -1;
    NppData     m_NppData     = {};
    HICON       m_hBtnIcon[4] = {};
    const bool &m_IsNppReady;

    // To handle doc panel resizing
    LONG m_lfDeltaWidth          = 0;
    LONG m_lfDeltaHeight         = 0;
    LONG m_lfInitialClientWidth  = 0;
    LONG m_lfInitialClientHeight = 0;
    RECT m_rcInitialWindowRect   = {};

    std::unique_ptr<ScintillaEditor> m_Editor    = nullptr;
    std::unique_ptr<TreeViewCtrl>    m_hTreeView = nullptr;
    std::shared_ptr<Setting>         m_pSetting  = nullptr;
};
